using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System;
using System.Collections.Generic;

namespace ExpressTest;
public static class DynamicFilterBuilder
{

    public static Expression<Func<Product, bool>> BuildDynamicFilter(string nameContains = null,
    decimal? minPrice = null,
    decimal? maxPrice = null,
    string category = null,
    bool? isInStock = null)
    {
        var criteria = new Dictionary<string, object>();
        //1. 创建参数表达式，代表传递给Lambda的单个Product对象
        ParameterExpression productParam = Expression.Parameter(typeof(Product), "p");

        //2. 创建一个初始的恒为真的表达式,相当于起始点（1==1）
        Expression combineBody = Expression.Constant(true);

        // 3.动态组合条件
        // - 如果提供了名称筛选条件，添加Name.Contains(nameContains)检查
        if (!string.IsNullOrEmpty(nameContains))
        {
            // product.Name
            MemberExpression nameProperty = Expression.Property(productParam, nameof(Product.Name));
            // "某词"
            ConstantExpression nameConstantPramas = Expression.Constant(nameContains, typeof(string));
            // product.Name.Contains("某词")
            MethodInfo containsMethod = typeof(string).GetMethod(nameof(string.Contains), new[] { typeof(string) });
            MethodCallExpression nameContainsExpr = Expression.Call(nameProperty, containsMethod, nameConstantPramas);
            // 与现有条件用 and 连接
            combineBody = Expression.AndAlso(combineBody, nameContainsExpr);
        }

        if (minPrice.HasValue)
        {
            MemberExpression priceProperty = Expression.Property(productParam, nameof(Product.Price));
            ConstantExpression minPriceConstant = Expression.Constant(minPrice.Value, typeof(decimal));
            BinaryExpression minPriceExpr = Expression.GreaterThanOrEqual(priceProperty, minPriceConstant);
            combineBody = Expression.AndAlso(combineBody, minPriceExpr);
        }

        if (maxPrice.HasValue)
        {
            MemberExpression priceProperty = Expression.Property(productParam, nameof(Product.Price));
            ConstantExpression maxPriceConstant = Expression.Constant(maxPrice.Value, typeof(decimal));
            BinaryExpression maxPriceExpr = Expression.LessThanOrEqual(priceProperty, maxPriceConstant);
            combineBody = Expression.AndAlso(combineBody, maxPriceExpr);
        }

        if (!string.IsNullOrEmpty(category))
        {
            MemberExpression categoryProperty = Expression.Property(productParam, nameof(Product.Category));
            ConstantExpression categoryConstant = Expression.Constant(category, typeof(string));
            BinaryExpression categoryExpr = Expression.Equal(categoryProperty, categoryConstant);
            combineBody = Expression.AndAlso(combineBody, categoryExpr);
        }

        if (isInStock.HasValue)
        {
            MemberExpression isInStockProperty = Expression.Property(productParam, nameof(Product.IsInStock));
            ConstantExpression isInStockConstant = Expression.Constant(isInStock.Value, typeof(bool));
            BinaryExpression isInStockExpr = Expression.Equal(isInStockProperty, isInStockConstant);
            combineBody = Expression.AndAlso(combineBody, isInStockExpr);
        }

        // 4. 创建Lambda表达式
        Expression<Func<Product, bool>> lambda = Expression.Lambda<Func<Product, bool>>(combineBody
            , productParam);
        return lambda;

    }

    public static Expression<Func<T, bool>> BuildDynamicFilter<T>(Dictionary<string, object> criteria)
    {
        var parameter = Expression.Parameter(typeof(T), "x");
        Expression combinedExpression = null;

        foreach (var criterion in criteria)
        {
            var property = typeof(T).GetProperty(criterion.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
            if (property == null) continue;

            var member = Expression.Property(parameter, property);
            var constant = Expression.Constant(Convert.ChangeType(criterion.Value, property.PropertyType));

            Expression comparison = null;
            if (property.PropertyType == typeof(string))
            {
                var method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
                comparison = Expression.Call(member, method, constant);
            }
            else if (property.PropertyType == typeof(int) || property.PropertyType == typeof(decimal) || property.PropertyType == typeof(double) || property.PropertyType == typeof(float))
            {
                comparison = Expression.Equal(member, constant);
            }
            else if (property.PropertyType == typeof(bool))
            {
                comparison = Expression.Equal(member, constant);
            }
            else
            {
                continue; // Unsupported type
            }

            combinedExpression = combinedExpression == null ? comparison : Expression.AndAlso(combinedExpression, comparison);
        }

        if (combinedExpression == null)
        {
            return x => true; // No criteria means no filtering
        }

        return Expression.Lambda<Func<T, bool>>(combinedExpression, parameter);
    }
}